from flask import Flask, request, send_file, render_template, jsonify
import os
# 移除tempfile依赖，使用当前目录的cache文件夹
import uuid
import time
from pdf2docx import Converter
import logging
from datetime import datetime, timedelta
import shutil
from PyPDF2 import PdfReader

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

app = Flask(__name__)

# 配置项
app.config['UPLOAD_FOLDER'] = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'cache')
app.config['MAX_CONTENT_LENGTH'] = 500 * 1024 * 1024  # 500MB
app.config['CONVERSION_TIMEOUT'] = 300  # 5分钟
app.config['MAX_CONCURRENT_TASKS'] = 3
app.config['FILE_RETENTION_HOURS'] = 24  # 成功文件保留24小时
app.config['FAILED_FILE_RETENTION_HOURS'] = 1  # 失败文件保留1小时

# 确保临时文件夹存在
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)

# 任务状态跟踪
active_tasks = {}  # 存储当前活动任务

def clean_old_files():
    """清理过期的临时文件"""
    now = datetime.now()
    success_retention = timedelta(hours=app.config['FILE_RETENTION_HOURS'])
    failed_retention = timedelta(hours=app.config['FAILED_FILE_RETENTION_HOURS'])

    for filename in os.listdir(app.config['UPLOAD_FOLDER']):
        file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        if os.path.isfile(file_path):
            modified_time = datetime.fromtimestamp(os.path.getmtime(file_path))
            # 检查文件类型和状态
            if filename.endswith('.docx'):
                # 成功转换的文件
                if now - modified_time > success_retention:
                    os.remove(file_path)
                    logger.info(f'已删除过期成功文件: {file_path}')
            elif filename.endswith('.pdf') or filename.endswith('.error'):
                # 上传的PDF或转换失败的文件
                if now - modified_time > failed_retention:
                    os.remove(file_path)
                    logger.info(f'已删除过期失败文件: {file_path}')

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/upload', methods=['POST'])
def upload():
    # 检查并发任务数
    if len(active_tasks) >= app.config['MAX_CONCURRENT_TASKS']:
        return jsonify({
            'success': False,
            'message': f'当前转换任务过多，请稍后再试。最大并发任务数: {app.config["MAX_CONCURRENT_TASKS"]}'
        }), 429

    if 'pdf_file' not in request.files:
        return jsonify({'success': False, 'message': '未找到上传的文件'}), 400

    file = request.files['pdf_file']
    if file.filename == '':
        return jsonify({'success': False, 'message': '未选择文件'}), 400

    if not file.filename.lower().endswith('.pdf'):
        return jsonify({'success': False, 'message': '仅支持PDF文件上传'}), 400

    try:
        # 生成唯一ID
        task_id = str(uuid.uuid4())
        original_filename = file.filename
        pdf_path = os.path.join(app.config['UPLOAD_FOLDER'], f'{task_id}.pdf')

        # 保存文件
        file.save(pdf_path)
        logger.info(f'已上传文件: {original_filename}, 任务ID: {task_id}')

        # 添加到活动任务
        active_tasks[task_id] = {
            'status': 'uploaded',
            'filename': original_filename,
            'upload_time': time.time(),
            'pdf_path': pdf_path
        }

        return jsonify({
            'success': True,
            'task_id': task_id,
            'message': '文件上传成功，准备转换'
        })
    except Exception as e:
        logger.error(f'上传文件时出错: {str(e)}')
        return jsonify({'success': False, 'message': f'上传文件时出错: {str(e)}'}), 500

@app.route('/convert/<task_id>', methods=['POST'])
def convert(task_id):
    if task_id not in active_tasks:
        return jsonify({'success': False, 'message': '无效的任务ID'}), 404

    task = active_tasks[task_id]
    if task['status'] != 'uploaded':
        return jsonify({'success': False, 'message': '任务已在处理中或已完成'}), 400

    try:
        # 更新任务状态
        task['status'] = 'converting'
        task['start_time'] = time.time()
        logger.info(f'开始转换任务: {task_id}, 文件名: {task["filename"]}')

        pdf_path = task['pdf_path']
        docx_path = os.path.join(app.config['UPLOAD_FOLDER'], f'{task_id}.docx')

        # 使用PyPDF2检查PDF文件是否有效
        def is_valid_pdf(file_path):
            try:
                reader = PdfReader(file_path)
                num_pages = len(reader.pages)
                return num_pages > 0
            except Exception as e:
                logger.error(f'检查PDF有效性时出错: {str(e)}')
                return False

        # 执行转换并跟踪进度
        cv = Converter(pdf_path)
        total_pages = len(cv.pages)
        
        # 检查PDF文件是否有效（页数大于0）
        # 添加额外检查以防止pdf2docx返回0页的情况
        if total_pages == 0:
            cv.close()
            raise ValueError('PDF文件没有有效页面，无法转换')
        
        # 定义进度更新函数
        def update_progress(page_num):
            # 防止除零错误
            if total_pages > 0:
                progress = int((page_num / total_pages) * 100)
            else:
                progress = 0
            task['progress'] = progress
            logger.info(f'转换进度: {task_id}, 页码: {page_num}/{total_pages}, 进度: {progress}%')

        # 初始进度
        update_progress(0)

        # 执行转换，逐页处理以便跟踪进度
        for page_num in range(total_pages):
            cv.convert(docx_path, start=page_num, end=page_num+1, overwrite=page_num==0)
            update_progress(page_num+1)

        cv.close()

        # 更新任务状态
        task['status'] = 'completed'
        task['end_time'] = time.time()
        task['docx_path'] = docx_path
        task['convert_time'] = task['end_time'] - task['start_time']
        logger.info(f'转换完成: {task_id}, 耗时: {task["convert_time"]:.2f}秒')

        # 删除PDF文件
        os.remove(pdf_path)

        return jsonify({
            'success': True,
            'task_id': task_id,
            'message': '文件转换成功',
            'convert_time': task['convert_time']
        })
    except Exception as e:
        logger.error(f'转换文件时出错: {str(e)}')
        # 标记任务失败
        task['status'] = 'failed'
        task['error'] = str(e)
        task['end_time'] = time.time()
        # 创建错误标记文件
        with open(os.path.join(app.config['UPLOAD_FOLDER'], f'{task_id}.error'), 'w') as f:
            f.write(str(e))
        return jsonify({'success': False, 'message': f'转换文件时出错: {str(e)}'}), 500

@app.route('/status/<task_id>')
def status(task_id):
    if task_id not in active_tasks:
        return jsonify({'success': False, 'message': '无效的任务ID'}), 404

    task = active_tasks[task_id]
    status_info = {
        'success': True,
        'task_id': task_id,
        'status': task['status'],
        'filename': task['filename']
    }

    # 如果任务正在转换中，添加进度信息
    if task['status'] == 'converting' and 'progress' in task:
        status_info['progress'] = task['progress']

    if task['status'] == 'failed' and 'error' in task:
        status_info['error'] = task['error']

    return jsonify(status_info)

@app.route('/download/<task_id>')
def download(task_id):
    if task_id not in active_tasks:
        return jsonify({'success': False, 'message': '无效的任务ID'}), 404

    task = active_tasks[task_id]
    if task['status'] != 'completed' or 'docx_path' not in task:
        return jsonify({'success': False, 'message': '文件尚未转换完成或转换失败'}), 400

    docx_path = task['docx_path']
    original_filename = task['filename']
    download_filename = os.path.splitext(original_filename)[0] + '.docx'

    # 删除任务记录
    del active_tasks[task_id]

    # 清理过期文件
    clean_old_files()

    return send_file(docx_path, as_attachment=True, download_name=download_filename)

@app.route('/health')
def health():
    return jsonify({'success': True, 'message': '服务器运行正常'})

if __name__ == '__main__':
    # 启动前清理过期文件
    clean_old_files()
    # 使用多线程处理并发请求
    app.run(host='0.0.0.0', port=5000, debug=False, threaded=True)